package top.chengdongqing.mishop.product

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.springframework.cache.annotation.Cacheable
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import top.chengdongqing.mishop.common.pageable.Page1
import top.chengdongqing.mishop.entities.Product
import top.chengdongqing.mishop.entities.ProductBrand
import top.chengdongqing.mishop.entities.ProductCategory
import top.chengdongqing.mishop.enums.BannerType
import top.chengdongqing.mishop.media.banner.BannerVO
import top.chengdongqing.mishop.product.dto.ProductSearchDTO
import top.chengdongqing.mishop.product.exception.ProductNotFoundException
import top.chengdongqing.mishop.product.vo.ProductDetailsVO
import top.chengdongqing.mishop.product.vo.ProductParentVO
import top.chengdongqing.mishop.product.vo.ProductSKUVO
import top.chengdongqing.mishop.product.vo.ProductVO
import top.chengdongqing.mishop.product.vo.RecommendedProductVO
import top.chengdongqing.mishop.repositories.BannerRepository
import top.chengdongqing.mishop.repositories.ProductBrandRepository
import top.chengdongqing.mishop.repositories.ProductCategoryRepository
import top.chengdongqing.mishop.repositories.ProductRepository

@Service
class ProductService(
    private val productRepository: ProductRepository,
    private val categoryRepository: ProductCategoryRepository,
    private val brandRepository: ProductBrandRepository,
    private val bannerRepository: BannerRepository
) {

    @Cacheable(value = ["products:categories"], key = "#limits + ':' + #itemsLimits + ':' + #withBanners")
    fun findCategories(limits: Int, itemsLimits: Int, withBanners: Boolean): List<ProductParentVO> {
        // 查询商品一级类别
        val pageRequest = PageRequest.ofSize(limits).withSort(Sort.by(ProductCategory::sortNo.name).descending())
        val categories = categoryRepository.findAllByParentId(0, pageRequest)

        return categories.map {
            // 查询下属类别及其关联商品
            val children = categoryRepository.findAllByParentId(it.id, pageRequest)
            val childrenVOs = (children.ifEmpty { listOf(it) }).map { child ->
                val categoryVO = ProductParentVO(
                    id = child.id,
                    name = child.name
                )
                if (itemsLimits > 0) {
                    val products = productRepository.findAllByCategoryId(
                        child.id,
                        PageRequest.ofSize(itemsLimits)
                    )
                    categoryVO.items = ProductVO.of(products)
                }
                categoryVO
            }

            // 查询关联的广告图
            val banners = if (withBanners) bannerRepository.findAllByTypeAndEnabledTrueAndAssociatedIdOrderBySortNo(
                BannerType.PRODUCT_CATEGORY,
                it.id
            ) else listOf()

            ProductParentVO(
                id = it.id,
                name = it.name,
                banners = BannerVO.of(banners),
                children = childrenVOs
            )
        }
    }

    @Cacheable(value = ["products:brands"], key = "#limits + ':' + #itemsLimits")
    fun findBrands(limits: Int, itemsLimits: Int): List<ProductParentVO> {
        // 查询商品品牌
        val pageRequest = PageRequest.ofSize(limits).withSort(Sort.by(ProductBrand::sortNo.name))
        val brands = brandRepository.findAll(pageRequest).content

        // 查询关联的商品
        return brands.map {
            val brandVO = ProductParentVO(
                id = it.id,
                name = it.name
            )
            if (itemsLimits > 0) {
                val products =
                    productRepository.findAllByBrandId(it.id, PageRequest.ofSize(itemsLimits))
                brandVO.items = ProductVO.of(products)
            }
            brandVO
        }
    }

    @Cacheable(value = ["products:names:hot"], key = "#limits")
    fun findNamesHot(limits: Int): List<String> {
        val sort = Sort.by(Product::sales.name).descending()
            .and(Sort.by(Product::createdAt.name).descending())
        val pageRequest = PageRequest.ofSize(limits).withSort(sort)
        return productRepository.findNamesByNameLike(null, pageRequest)
    }

    @Cacheable(value = ["products:names:like"], key = "#keyword")
    fun findNamesLike(keyword: String): List<String> {
        return productRepository.findNamesByNameLike(keyword, PageRequest.ofSize(10))
    }

    fun searchProducts(searchDTO: ProductSearchDTO, pageable: Pageable): Page1<ProductVO> {
        val page = productRepository.findAllByPage(searchDTO, pageable).map {
            val gallery = it.skus.map { sku -> sku.pictureUrl }.distinct()
            ProductVO.of(it, gallery)
        }
        return Page1.of(page)
    }

    @Cacheable(value = ["products:recommended"], key = "#limits")
    fun findRecommended(limits: Int): List<RecommendedProductVO> {
        return productRepository.findRecommended(PageRequest.ofSize(limits)).map { RecommendedProductVO.of(it) }
    }

    @Cacheable(value = ["products"], key = "#id")
    fun findDetails(id: Int): ProductDetailsVO {
        val product = productRepository.findByIdAndEnabledTrue(id)
            ?: throw ProductNotFoundException("该商品不存在")

        return ProductDetailsVO(
            product.id,
            product.name,
            product.description,
            product.brand.name,
            product.category.name,
            product.staticDetails?.let { jacksonObjectMapper().readValue(product.staticDetails!!) },
            product.skus.map { ProductSKUVO.of(it) }
        )
    }
}